package com.example.mykotlin.service

import com.example.mykotlin.base.result.ServiceException
import com.example.mykotlin.base.util.GetBean
import com.example.mykotlin.base.util.RedissonUtil
import com.example.mykotlin.base.util.tryOnly
import com.example.mykotlin.model.pojo.CacheData
import com.fasterxml.jackson.core.type.TypeReference
import org.springframework.cache.set
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class CommonService {
    @Transactional(rollbackFor = [Throwable::class])
    fun <R : Any> executeTransaction(service: () -> R?) = service()


    fun <E : Any> localCache(managerName: String, key: String, service: () -> E?): E? {
        val cache = GetBean.caffeineCacheManager().getCache(managerName)!!
        var cacheValue = cache.get(key, String::class.java)
        if (cacheValue == null) {
            val getLock = RedissonUtil.tryLockAndGetLock(managerName + key, 0, 10)
            if (getLock.second) {
                try {
                    cacheValue = cache.get(key, String::class.java)
                    if (cacheValue == null) {
                        try {
                            val r = service()
                            cache[key] = GetBean.objectMapper().writeValueAsString(CacheData(value = r))
                            return r
                        } catch (e: Exception) {
                            cache[key] = GetBean.objectMapper().writeValueAsString(CacheData<E>(true))
                            throw ServiceException("业务异常")
                        }
                    } else {
                        val cacheData =
                            GetBean.objectMapper().readValue(cacheValue, object : TypeReference<CacheData<E?>>() {})
                        if (cacheData.e) {
                            throw ServiceException("业务异常")
                        } else {
                            return cacheData.value
                        }
                    }
                } finally {
                    RedissonUtil.unlock(getLock.first)
                }
            } else {
                tryOnly { Thread.sleep(10) }
                return localCache(managerName, key, service)
            }
        } else {
            val cacheData = GetBean.objectMapper().readValue(cacheValue, object : TypeReference<CacheData<E?>>() {})
            if (cacheData.e) {
                throw ServiceException("业务异常")
            } else {
                return cacheData.value
            }
        }
    }
}